#!/usr/bin/python3 
# -*- coding: utf-8 -*-
"""
@File Name  : _root.py
@Author     : LeeCQ
@Date-Time  : 2019/12/31 19:16


根模块(root):
    1. 9CCMS数据的更新 - Python 实现
        1.1. 数据更新位置可选 -- 这个位置应该是全局的位置。
        1.2.

    2. 数据读取。
        2.1. 按需读取，

    3. 数据的对应关系 -- 对应关系写到数据库中；

    4. requests根的设计。 def request_get()

    5. 数据库的相关操作 - db: 9C
        5.1.  photo/ txt/ m3u8/ torrent

    6. 所有的错误类

"""
import sys, logging

logger = logging.getLogger("logger")  # 创建实例
formatter = logging.Formatter("[%(asctime)s] < %(funcName)s - %(thread)d > [%(levelname)s] %(message)s")
# 终端日志
consle_handler = logging.StreamHandler(sys.stdout)
consle_handler.setFormatter(formatter)  # 日志文件的格式
logger.setLevel(logging.DEBUG)  # 设置日志文件等级

import requests, urllib3, time, json, zipfile, os
#
from SQL.MySQL.tencent_MySQL import TencentMySQL


DATA_PATH = './sup/data-path.ini'
sort_relation = [
    {"id": "0", "name": "复位字符", "type": "None"},
    {"id": "1", "name": "亚洲情色", "type": "video"},
    {"id": "2", "name": "热门女优", "type": "video"},
    {"id": "3", "name": "中文字幕", "type": "video"},
    {"id": "4", "name": "国产自拍", "type": "video"},
    {"id": "5", "name": "无码专区", "type": "video"},
    {"id": "6", "name": "伦理三级", "type": "video"},
    {"id": "7", "name": "制服师生", "type": "video"},
    {"id": "8", "name": "强奸乱伦", "type": "video"},
    {"id": "9", "name": "丝袜美腿", "type": "video"},
    {"id": "10", "name": "人妻熟女", "type": "video"},
    {"id": "11", "name": "欧美性爱", "type": "video"},
    {"id": "12", "name": "4K岛国", "type": "video"},
    {"id": "13", "name": "韩国资源", "type": "video"},
    {"id": "14", "name": "卡通动漫", "type": "video"},
    {"id": "15", "name": "人妖人兽", "type": "video"},
    {"id": "16", "name": "男同女同", "type": "video"},
    {"id": "17", "name": "VR资源", "type": "video"},
    {"id": "18", "name": "JAV高清", "type": "video"},

    {"id": "19", "name": "激情都市", "type": "book"},
    {"id": "20", "name": "家庭乱伦", "type": "book"},
    {"id": "21", "name": "青春校园", "type": "book"},
    {"id": "22", "name": "武侠虚幻", "type": "book"},
    {"id": "23", "name": "人妻熟女", "type": "book"},
    {"id": "24", "name": "强暴虐待", "type": "book"},
    {"id": "25", "name": "GIF动图", "type": "pic"},
    {"id": "26", "name": "唯美写真", "type": "pic"},
    {"id": "27", "name": "网友自拍", "type": "pic"},
    {"id": "28", "name": "露出激情", "type": "pic"},
    {"id": "29", "name": "街拍偷拍", "type": "pic"},
    {"id": "30", "name": "女优咨询", "type": "pic"},
    {"id": "31", "name": "午夜故事", "type": "radio"},
    {"id": "32", "name": "激情骚麦", "type": "radio"},
    {"id": "33", "name": "淫词艳曲", "type": "radio"},
    {"id": "34", "name": "枕边男女", "type": "radio"},
    {"id": "35", "name": "蓝光无码", "type": "bt"},
    {"id": "36", "name": "蓝光有码", "type": "bt"}
    ]


class Error9C(Exception): pass
class RetryOut(Error9C): pass
class APICEYFileError(Error9C): pass
#


class API:
    """通过API的idd获取返回值

    有条件的话改为动态的；

    txt = API.CEY["txtapi"]
    pic = "imgapi"
    live = "zhiboapi"
    """
    src = 'http://api.9ccmsapi.com/9CCMS18/API/index.php'   # 最重要 更新源
    pic = '//img.9ccmsapi.com/9CCMS18/jiexi.php?a_id='      # 图片的解析API
    txt = '//txt.up8.app/9ccms18/cj/jiexi.php?a_id='        # 文本的解析API
    vid = ''  # 视频直接是m3u8地址,无需使用API解析！            # 视屏的解析API


class NineCSQL(TencentMySQL):
    """9C相关的数据库接口

    数据库位置 TencentMySQL.9C.*
    内容：每次更新的数据打包到MySQL中！
    """
    def __init__(self):
        super().__init__('u', 'p', 'd')


class Root9C:
    """9C基类：

    """

    def __init__(self, retry=5, timeout=10, debug_level=0,
                 strict_mode=True, data_path=DATA_PATH):
        self.retry = retry
        self.timeout = timeout
        self.strictMode = strict_mode  # 遇到问题直接报错！
        self.debug_level = debug_level if debug_level > 4 else 4
        self.data_path = data_path

    def set_debugLevel(self, level):
        self.debug_level = level

    # 请求的统一接口
    def request_get(self, url: str, header: dict, **kwargs):
        """request.get的统一入口；

        :param url:
        :param header:
        :return: Response对象 - 响应结果 - 200 OK
        """
        n = self.retry
        header.setdefault('User-Agent',
                          'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362')
        _s = time.time()
        urllib3.disable_warnings()
        while n:
            n -= 1
            try:
                __res = requests.get(url, headers=header, verify=False, timeout=self.timeout, **kwargs)
                if __res.status_code == 200:
                    if self.debug_level >= 2:
                        logger.info(f'URL花费{time.time() - _s}s(失败{self.retry - n - 1}次)正常返回:{url}')
                    return time.time() - _s, __res
                else:
                    if self.debug_level >= 1:
                        logger.warning(f'URL异常返回码({__res.status_code}): {url}')
            except requests.exceptions.ReadTimeout and requests.exceptions.ConnectTimeout:
                if self.debug_level >= 1:
                    logger.warning(f'URL请求超时: {url}')
            except:
                if self.debug_level >= 0:
                    logger.error(f'请求URL时发生了未知的错误（{sys.exc_info()}）: {url}')
                if self.strictMode: raise
        logger.error(f'重试结束，依旧失败: {url}')
        if self.strictMode:
            raise
        else:
            return -1, RetryOut

    # 获取数据情况
    def read_data(self, id_: int):
        """把数据文件转换为JSON并返回。

        通过文件ID要哪个返回哪个！
        """
        with open(self.data_path, 'r') as p:
            path = p.read()
        name = path + '/' + str(id_) + '.txt'       # 拼接文件名
        with open(name, 'r', encoding='utf8') as ff:
            return json.loads(ff.read())


class NineCSource(Root9C):
    """9C更新数据 - 数据txt文件

    """

    def __init__(self, path='./data'):
        super().__init__()
        self.path = path if path.endswith('/') else path + '/'
        os.makedirs(self.path, exist_ok=True)

    def get_apiCey(self):
        _, cey = self.request_get(API.src, {})
        if _ >= 0:
            open(self.path + 'API.CEY', 'w', encoding='utf8').write(cey.text)  # 写入文件
            cey = json.loads(cey.text)
            return cey
        else:
            raise APICEYFileError('API.CEY文件获取失败...')

    def get_zip(self):
        """下载并解压源文件。"""
        apiDic = self.get_apiCey()
        for _zip in apiDic['zip']:
            _, file = self.request_get(_zip, {})
            if _ >= 0:
                zip_name = self.path + _zip.split('/')[-1]
                with open(zip_name, 'wb') as zf:  # 保存
                    zf.write(file.content)
                with zipfile.ZipFile(zip_name, 'r') as zf:  # 解压
                    for file in zf.namelist():
                        with open(self.path + file, 'wb') as tf:  # 写入解压后文件
                            tf.write(zf.read(file))
                os.remove(zip_name)
            else:
                logger.error(f'更新源文件({_zip})失败')
                if self.strictMode:
                    logger.error(f'raise in "_root.NineCSourceUpdate.get_zip": 源文件更新失败')
                    raise

    def update(self):
        self.get_zip()
        with open(DATA_PATH, 'w') as f:
            f.write(os.path.abspath(self.path))
        return 0

    # def read_file(self, id_: int):
    #     """把数据文件转换为JSON并返回。
    #
    #     通过文件ID要哪个返回哪个！
    #     """
    #     name = self.path + str(id_)
    #     with open(name, 'r', encoding='utf8') as ff:
    #         return json.loads(ff.read())


if __name__ == '__main__':
    logger.addHandler(consle_handler)  # 添加句柄
    NineCSource().update()
